﻿using GsOrganization.Common.Extend;
using GsOrganization.Common.Helper;
using GsOrganization.TcpPacket;
using GsOrganization.TcpPacket.PayLoad;
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;

namespace GsOrganization.TcpClient
{
    /***********************
     * 
     * 构造方法后，start一次即可
     * 如果确定需要断开连接，调用close
     * 
     * OnConnectionStateChange 回调需要处理，当参数为1时，需要立刻发送设备登录消息。
     * 
     * ********************/
    public class ClientSDK
    {
        private ILogger logger;
        public Action<ushort, string> OnRecvServerOrder { get; set; }

        ReaderWriterLock RWL_sl_TCPHandlers = new ReaderWriterLock();
        /// <summary>
        /// TCP指令集
        /// </summary>
        public SortedList<ushort, Action<string>> sl_TCPHandlers = new SortedList<ushort, Action<string>>();

        public Action<int> OnConnectionStateChange { get; set; }
        public CommonTcpClientLib commonTcpClientLib;
        private string ServerIPAddress;
        private int ServerPort;
        private byte[] LastBytes = new byte[0];
        /// <summary>
        /// 当前状态
        /// </summary>
        private int NowState = 0;
        /// <summary>
        /// 当前登录状态
        /// </summary>
        private int LoginState = 0;
        /// <summary>
        /// 连接断开时重连
        /// </summary>
        private bool ContinueConnect = true;
        private ReaderWriterLock RWL = new ReaderWriterLock();
        AutoResetEvent ARE_Restart = new AutoResetEvent(false);
        private Thread ThreadConnect;
        private Thread Thread_SendHeartBeat;
        private DateTime LastSendDataTime = DateTime.Now.AddSeconds(-30);
        private DateTime LastRecvDataTime = DateTime.Now.AddSeconds(-30);
        private int MaxWaitRecvTime = 120 * 5;
        private int PingTime = 30;


        private void SetLastSendDataTime()
        {
            RWL.AcquireWriterLock(-1);
            LastSendDataTime = DateTime.Now;
            RWL.ReleaseWriterLock();
        }
        private int GetTotalSendDataTimeRemain()
        {
            RWL.AcquireWriterLock(-1);
            var res = Math.Abs((DateTime.Now - LastSendDataTime).TotalSeconds);
            RWL.ReleaseWriterLock();
            return (int)res;
        }
        private void SetRecvSendDataTime()
        {
            RWL.AcquireWriterLock(-1);
            LastRecvDataTime = DateTime.Now;
            RWL.ReleaseWriterLock();
        }
        private int GetTotalRecvDataTimeRemain()
        {
            RWL.AcquireWriterLock(-1);
            var res = Math.Abs((DateTime.Now - LastRecvDataTime).TotalSeconds);
            RWL.ReleaseWriterLock();
            return (int)res;
        }

        private string CommonTcpId = "";

        private string GetCommonTcpId()
        {
            RWL.AcquireReaderLock(-1);
            string s = CommonTcpId;
            RWL.ReleaseReaderLock();
            return s;
        }

        private string SetCommonTcpId()
        {
            RWL.AcquireWriterLock(-1);
            string s = Guid.NewGuid().ToString();
            CommonTcpId = s;
            RWL.ReleaseWriterLock();
            return s;
        }


        private static ClientSDK? _ClientSDK = null;
        public static ClientSDK GetClientSDK() => _ClientSDK;
        public static ClientSDK GetClientSDK(ILogger logger, string ServerIPAddress, int ServerPort)
        {
            if (_ClientSDK == null)
                _ClientSDK = new ClientSDK(logger, ServerIPAddress, ServerPort);
            return _ClientSDK;
        }



        public ClientSDK(ILogger logger, string ServerIPAddress, int ServerPort)
        {
            this.logger = logger;
            OnConnectionStateChange = TCPStateChanged;
            OnRecvServerOrder = TCPRecvMsg;
            this.ServerIPAddress = ServerIPAddress;
            this.ServerPort = ServerPort;
        }


        /// <summary>
        /// TCP收到命令
        /// </summary>
        /// <param name="msgId"></param>
        /// <param name="msg"></param>
        public void TCPRecvMsg(ushort msgId, string msg)
        {

            var action = GetTcpHandler(msgId);
            try
            {
                action?.Invoke(msg);
            }
            catch (Exception ex)
            {
                //  LogHelper./*Error*/(ex, "[MainWindow]TCP通讯接收命令异常");
            }
        }

        /// <summary>
        /// 获取TCP处理器
        /// </summary>
        /// <param name="CmdId"></param>
        /// <returns></returns>
        private Action<string> GetTcpHandler(ushort CmdId)
        {
            Action<string> action = null;
            RWL_sl_TCPHandlers.AcquireWriterLock(-1);
            if (sl_TCPHandlers.ContainsKey(CmdId))
                action = sl_TCPHandlers[CmdId];
            RWL_sl_TCPHandlers.ReleaseWriterLock();
            return action;
        }


        /// <summary>
        /// TCP连接状态变更
        /// </summary>
        /// <param name="state"></param>
        public async void TCPStateChanged(int state)
        {
            try
            {
                if (state == 1)
                {
                    #region Send Login
                    logger?.Information("[TCP] ToSendLogin");
                    DeviceLoginReq loginReq = new DeviceLoginReq()
                    {
                        DeviceCode = SystemHelper.GetDeviceCode(),
                        HardDiskId = SystemHelper.GetDiskInfo()[0],
                        MainBoardId = SystemHelper.GetMainBoardId()
                    };
                    logger?.Information($"[TCP] ToSendLogin LoginReq = {loginReq.ToJsonString()}");
                    MessagePacket loginPacket = new MessagePacket(MessageCommandId.DeviceLoginReq, loginReq.ToJsonString());
                    int loginResult = SendMessage(loginPacket);
                    #endregion
                }
                else
                {
                    logger?.Information($"TCP连接状态变更->进入离线模式");
                }
            }
            catch (Exception ex)
            {
                logger?.Error(ex, ex.Message);
            }
        }

        private void SendHeartBeat()
        {
            long cnt = 0;
            long cnt_getstate = 0;
            while (true)
            {
                Thread.Sleep(15000);
                if (Interlocked.Increment(ref cnt) % 60 == 0)
                {
                    LogInformation("SendHeartBeat start");
                }
                if (GetConnectState() == 0 || GetLoginState() == 0)
                {
                    if (Interlocked.Increment(ref cnt) % 60 == 0)
                    {
                        LogInformation("SendHeartBeat [GetConnectState=0] or [GetLoginState=0]");
                    }
                    continue;
                }
                if (GetTotalRecvDataTimeRemain() > MaxWaitRecvTime)
                {
                    //超时没有收到消息，认为TCP已断
                    LogInformation("SendHeartBeat Time out!");
                    try
                    {
                        commonTcpClientLib.Close();
                    }
                    catch
                    { }
                }
                if (GetTotalSendDataTimeRemain() > PingTime)
                {

                    LogInformation("SendHeartBeat send Ping!");
                    Header Ping = new Header()
                    {
                        CommandId = MessageCommandId.HeartbeatReq,
                        MessageLength = IHeader.HeaderLength,

                        TimeStamp = (int)(DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds
                    };
                    try
                    {
                        commonTcpClientLib.SendMessage(Ping.ToBytes());
                        SetLastSendDataTime();
                    }
                    catch
                    { }
                }

            }
        }




        /// <summary>
        /// 发送报文消息,0失败，大于0成功
        /// </summary>
        /// <param name="MessagePacket"></param>
        /// <returns></returns>
        public int SendMessage(IPacket MessagePacket)
        {
            try
            {
                if (GetConnectState() == 0)
                {
                    return -1;
                }

                var dsfa = commonTcpClientLib.ClientSocket.Connected;
                var result = commonTcpClientLib.SendMessage(MessagePacket.ToBytes());
                //if (result <= 0)
                //{
                //    this.commonTcpClientLib.Close();
                //    return result;
                //}
                SetLastSendDataTime();
                LogInformation($"[ClientSDK][SendData][Cmd={MessagePacket.Header.CommandId.ToString("X4")}][msgLen={MessagePacket.Header.MessageLength}][result={result}]");
                return result;
            }
            catch (Exception ex)
            {
                LogError(ex, $"[ClientSDK][SendData][Cmd={MessagePacket.Header.CommandId.ToString("X4")}][msgLen={MessagePacket.Header.MessageLength}]");
                return 0;
            }
        }

        public int SendBytes(byte[] bsData)
        {
            var result = commonTcpClientLib.SendMessage(bsData);
            return result;
        }


        /// <summary>
        /// Start后，开始连接服务器，包含断线重连
        /// </summary>
        public void Start()
        {
            SetIsRestart(true);
            if (ThreadConnect == null)
            {
                ThreadConnect = new Thread(ConnectServer);
                ThreadConnect.IsBackground = true;
                ThreadConnect.Start();
            }
            if (Thread_SendHeartBeat == null)
            {
                Thread_SendHeartBeat = new Thread(SendHeartBeat);
                Thread_SendHeartBeat.IsBackground = true;
                Thread_SendHeartBeat.Start();
            }
        }
        private void ConnectServer()
        {
            while (true)
            {
                LogInformation("[ClientSDK] to Connect !");
                if (!BeginConnect())
                {
                    LogInformation("[ClientSDK] Connect failt!");
                    if (OnConnectionStateChange != null)
                        OnConnectionStateChange(0);
                    Thread.Sleep(1000);
                    continue;
                }
                LogInformation("[ClientSDK] Connect Success");
                SetRecvSendDataTime();
                SetLastSendDataTime();
                SetConnectState(1);
                try
                {
                    if (OnConnectionStateChange != null)
                        OnConnectionStateChange(1);
                }
                catch
                { }
                //连接成功，卡住异步
                ARE_Restart.WaitOne(-1);
                commonTcpClientLib.Close();
                Thread.Sleep(10000);
                if (!GetIsRestart())
                {
                    return;
                }
            }
        }

        private bool BeginConnect()
        {
            LastBytes = new byte[0];
            try
            {
                var ips = Dns.GetHostAddresses(ServerIPAddress);
                if (ips.Length == 0)
                {
                    return false;
                }
                commonTcpClientLib = new CommonTcpClientLib(ips[0], ServerPort, RecvServerData, ConnectClose, SetCommonTcpId());
            }
            catch
            {
                return false;
            }
            return commonTcpClientLib.Connect(5000);
        }


        /// <summary>
        /// Close调用后，断开连接，且不再重新连接
        /// </summary>
        public void Close()
        {
            SetIsRestart(false);
            try
            {
                commonTcpClientLib.Close();
            }
            catch
            { }
        }

        #region [消息事件]

        /// <summary>
        /// 接收到对端的消息
        /// </summary>
        /// <param name="bsData"></param>
        private void RecvServerData(byte[] bsData)
        {
            SetRecvSendDataTime();
            byte[] bsTotal = new byte[bsData.Length + LastBytes.Length];
            LastBytes.CopyTo(bsTotal, 0);
            bsData.CopyTo(bsTotal, LastBytes.Length);
            LastBytes = new byte[0];
            int idx = 0;
            while (true)
            {
                if (bsTotal.Length - idx < IHeader.HeaderLength)
                {
                    LastBytes = new byte[bsTotal.Length - idx];
                    Array.Copy(bsTotal, idx, LastBytes, 0, LastBytes.Length);
                    break;
                }
                Header header = new Header(bsTotal, idx);
                if (idx + header.MessageLength > bsTotal.Length)
                {
                    LastBytes = new byte[bsTotal.Length - idx];
                    Array.Copy(bsTotal, idx, LastBytes, 0, LastBytes.Length);
                    break;
                }
                byte[] bs_Payload = new byte[header.MessageLength - 8];
                Array.Copy(bsTotal, idx + IHeader.HeaderLength, bs_Payload, 0, header.MessageLength - IHeader.HeaderLength);
                idx += header.MessageLength;
                try
                {
                    byte[] bsPayLoadDecrypt = bs_Payload.AES_Dencrypt();
                    LogInformation($"[ClientSDK][RecvData][Cmd={header.CommandId.ToString("X4")}][PayLoad={bsPayLoadDecrypt.UTF8ToString()}]");
                    OnRecvServerOrder(header.CommandId, bsPayLoadDecrypt.UTF8ToString());
                }
                catch (Exception ex)
                {
                    LogError(ex, $"[ClientSDK][RecvData][Cmd={header.CommandId.ToString("X4")}]");
                }
            }
        }

        private void ConnectClose(string Reason, string tcpid)
        {
            if (GetCommonTcpId() != tcpid)
            {
                return;
            }
            SetConnectState(0);
            if (OnConnectionStateChange != null)
            {
                OnConnectionStateChange(0);
            }
            if (GetIsRestart())
            {
                ARE_Restart.Set();
            }
        }

        private bool GetIsRestart()
        {
            RWL.AcquireWriterLock(-1);
            bool r = ContinueConnect;
            RWL.ReleaseWriterLock();
            return r;
        }

        private void SetIsRestart(bool r)
        {
            RWL.AcquireWriterLock(-1);
            ContinueConnect = r;
            RWL.ReleaseWriterLock();
        }

        /// <summary>
        /// 获取当前的连接状态，0-断连，1-已连接
        /// </summary>
        /// <returns></returns>
        public int GetConnectState()
        {
            RWL.AcquireWriterLock(-1);
            int r = NowState;
            RWL.ReleaseWriterLock();
            return r;
        }

        public int GetLoginState()
        {
            RWL.AcquireWriterLock(-1);
            int r = LoginState;
            RWL.ReleaseWriterLock();
            return r;
        }

        private void SetConnectState(int State)
        {
            RWL.AcquireWriterLock(-1);
            NowState = State;
            if (State == 0)
            {
                LoginState = 0;
            }
            RWL.ReleaseWriterLock();
        }

        public void SetLoginState(int State)
        {
            RWL.AcquireWriterLock(-1);
            LoginState = State;
            RWL.ReleaseWriterLock();
        }



        #endregion

        #region [异步发送消息]

        AutoResetEvent ARE_SendData = new AutoResetEvent(false);
        //public async Task<int> SendData(SendDataReq req)
        //{
        //    if (this.GetConnectState() == 0)
        //    {
        //        return -1;
        //    }
        //    if (req == null)
        //    {
        //        return -1;
        //    }
        //    MessagePacket messagePacket = new MessagePacket(MessageCommandId.SendDataReq, req.ToJsonString());
        //    return await Task.Run(() =>
        //    {
        //        this.RWL.AcquireWriterLock(-1);
        //        int t = this.commonTcpClientLib.SendMessage(messagePacket.ToBytes());
        //        this.RWL.ReleaseWriterLock();
        //        if (ARE_SendData.WaitOne(3000))
        //        {
        //            return 0;
        //        }
        //        return 1;
        //    });
        //}



        //public async Task<string> GetServerSession()
        //{
        //    if (this.GetConnectState() == 0)
        //    {
        //        return "";
        //    }
        //    Header head = new Header()
        //    {
        //        CommandId = MessageCommandId.GetServerSessionReq,
        //        MessageLength = IHeader.HeaderLength,
        //        TimeStamp = CommonHelper.GetTimeStampS()
        //    };

        //    return null;
        //}


        #endregion

        #region 日志处理
        private void LogInformation(string LogTxt)
        {
            try
            {
                logger?.Information(LogTxt);
            }
            catch
            { }
        }

        private void LogWarning(Exception ex, string LogTxt)
        {
            try
            {
                logger?.Warning(ex, LogTxt);
            }
            catch
            { }
        }

        private void LogError(Exception ex, string LogTxt)
        {
            try
            {
                logger?.Error(ex, LogTxt);
            }
            catch
            { }
        }
        #endregion

    }
}
